#include "rsacrypto.h"
#include <iostream>
#include <QDir>
#include <QFile>
#include <QDebug>

#ifdef Q_OS_WIN
extern "C"
{
#include <openssl/applink.c>
}
#endif

RsaCrypto::RsaCrypto()
{
    m_publicKey = RSA_new();
    m_privateKey = RSA_new();
}

RsaCrypto::~RsaCrypto()
{
    RSA_free(m_publicKey);
    RSA_free(m_privateKey);
}

void RsaCrypto::generateRsakey(int bits, QString pub, QString pri)
{
    QDir dir;
    if(!dir.exists(DirName))
    {
        dir.mkdir(DirName);
    }
    RSA* r = RSA_new();
    // 生成RSA密钥对
    // 创建bignum对象
    BIGNUM* e = BN_new();
    // 初始化bignum对象
    BN_set_word(e, 456787);
    RSA_generate_key_ex(r, bits, e, NULL);

    // 创建bio文件对象
    BIO* pubIO = BIO_new_file(pub.toStdString().data(), "w");
    // 公钥以pem格式写入到文件中
    PEM_write_bio_RSAPublicKey(pubIO, r);
    // 缓存中的数据刷到文件中
    BIO_flush(pubIO);
    BIO_free(pubIO);

    // 创建bio对象
    BIO* priBio = BIO_new_file(pri.toStdString().data(), "w");
    // 私钥以pem格式写入文件中
    PEM_write_bio_RSAPrivateKey(priBio, r, NULL, NULL, 0, NULL, NULL);
    BIO_flush(priBio);
    BIO_free(priBio);

    // 得到公钥和私钥
    m_privateKey = RSAPrivateKey_dup(r);
    m_publicKey = RSAPublicKey_dup(r);

    // 释放资源
    BN_free(e);
    RSA_free(r);
}

int RsaCrypto::parseStringToKey(QString keyStr, KeyType type)
{
    // 字符串存储在内存中 -> 内存对象中
    // 创建一个基于内存操作的BIO对象, 将要解析的字符串存储到这个BIO对象中即可
    QByteArray arr = keyStr.toUtf8();
    BIO* bio = BIO_new_mem_buf(arr.data(), arr.size());
    if (type == KeyType::Public)
    {
        // 公钥字符串的解析
        // 将bio中的pem数据读出
        if (PEM_read_bio_RSAPublicKey(bio, &m_publicKey, NULL, NULL) == NULL)
        {
            return -1;
        }
    }
    else
    {
        // 私钥字符串的解析
        // 将bio中的pem数据读出
        if (PEM_read_bio_RSAPrivateKey(bio, &m_privateKey, NULL, NULL) == NULL)
        {
            return -1;
        }
    }
    BIO_free(bio);
    return 0;
}

QString RsaCrypto::rsaPubKeyEncrypt(QString data)
{
    QByteArray arr = data.toUtf8();
    // 计算公钥长度
    int keyLen = RSA_size(m_publicKey);
    qDebug() << "pubKey len: " << keyLen;
    // 申请内存空间
    char* encode = new char[keyLen + 1];
    // 使用公钥加密
    int ret = RSA_public_encrypt(arr.size(),
                                 (const unsigned char*)arr.data(),
                                 (unsigned char*)encode, m_publicKey,
                                 RSA_PKCS1_PADDING);
    QByteArray retStr;
    if (ret >= 0)
    {
        // 加密成功, 转换为base64格式
        retStr = QByteArray(encode, ret).toBase64();
    }
    // 释放资源
    delete[]encode;
    return retStr;
}

QString RsaCrypto::rsaPriKeyDecrypt(QString encData)
{
    QByteArray arr = QByteArray::fromBase64(encData.toUtf8());
    // 计算私钥长度
    int keyLen = RSA_size(m_privateKey);
    // 使用私钥解密
    char* decode = new char[keyLen + 1];
    int ret = RSA_private_decrypt(arr.size(),
                                  (const unsigned char*)arr.data(),
                                  (unsigned char*)decode, m_privateKey,
                                  RSA_PKCS1_PADDING);
    arr.clear();
    if (ret >= 0)
    {
        arr = QByteArray(decode, ret);
    }
    delete[]decode;
    return arr;
}

QString RsaCrypto::rsaSign(QString data, SignLevel level)
{
    unsigned int len;
    QByteArray arr = data.toUtf8();
    char* signBuf = new char[RSA_size(m_privateKey)];
    RSA_sign(level, (const unsigned char*)arr.data(),
             arr.size(), (unsigned char*)signBuf,
             &len, m_privateKey);
    qDebug() << "sign len: " << len;
    // 签名数据转换为base64格式
    arr = QByteArray(signBuf, len).toBase64();
    delete []signBuf;
    return arr;
}

bool RsaCrypto::rsaVerify(QString data, QString signData, SignLevel level)
{
    // 签名数据base64解码并转换为 string
    QByteArray sign = QByteArray::fromBase64(signData.toUtf8());
    QByteArray arr = data.toUtf8();
    // 验证签名
    int ret = RSA_verify(level, (const unsigned char*)arr.data(),
                         arr.size(), (const unsigned char*)sign.data(),
                         sign.size(), m_publicKey);
    if (ret != 1)
    {
        return false;
    }
    return true;
}

QString RsaCrypto::publicPemKey(QString path)
{
    return getPemKey(path);
}

QString RsaCrypto::privatePemKey(QString path)
{
    return getPemKey(path);
}

QString RsaCrypto::getPemKey(QString path)
{
    QFile file(path);
    file.open(QFile::ReadOnly);
    QByteArray text = file.readAll();
    file.close();
    return text;
}
